﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NHibernate;

namespace AcademicPlanner.Data
{
	/// <summary>
	/// EntityRepository is the base class for repositories of data entities of
	/// the AcademicPlanner application. An EntityRepository provides means to
	/// retrieve and persist <c>Entity</c>s. It relies on NHibernate for persistence
	/// management.
	/// </summary>
	/// <seealso href="https://www.hibernate.org/343.html"/>
	/// <version>1.0</version>
	/// <since>1.0</since>
	public class EntityRepository
	{
		/// <summary>
		/// The prefix for this EntityRepository to be used when retrieving queries
		/// defined in NHibernate mappings.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		protected string namedQueriesPrefix = "";


		/// <summary>
		/// Gets the <c>DataSourceAccessSupervisor</c> of the data layer of the application.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		protected DataSourceAccessSupervisor DSAS { get { return DataSourceAccessSupervisor.GetInstance(); } }


		/// <summary>
		/// Gets a named query defined in the NHibernate mapping of this EntityRepository's <c>Entity</c>.
		/// This method requests the query from the current NHibernate session.
		/// </summary>
		/// <param name="pName">
		/// The name of the query; this is not the name as defined in the NHibernate mapping.
		/// The name defined in the NHibernate mapping is in the format
		///		<c>namedQueriesPrefix +"."+<paramref name="pName">name of the query</paramref></c>.
		/// </param>
		/// <returns>The query object.</returns>
		/// <exception cref="T:DataException">
		/// Thrown with an error code of <c>DataErrorCode.GET_NAMED_QUERY_FAILED</c>
		/// when an error is reported by NHibernate. The <c>HibernateException</c> is
		/// accessible via the <c>InnerException</c> property.
		/// </exception>
		/// <version>1.0</version>
		/// <since>1.0</since>
		protected IQuery GetQuery(string pName)
		{
			string vFullName = namedQueriesPrefix + "." + pName;
			IQuery rQuery = null;

			try
			{
				rQuery = DSAS.Session.GetNamedQuery(vFullName);
			}
			catch (HibernateException vHx)
			{
				var vDx = new DataException(DataErrorCode.GET_NAMED_QUERY_FAILED, vHx);
				throw vDx;
			}

			return rQuery;
		}

		/// <summary>
		/// Saves a <c>Entity</c> to the persistence layer.
		/// </summary>
		/// <param name="pEntity">The <c>Entity</c> to save.</param>
		/// <exception cref="T:DataException">
		/// Thrown with an error code of <c>DataErrorCode.SAVE_ENTITY_FAILED</c>
		/// when an error is reported by NHibernate. The <c>HibernateException</c> is
		/// accessible via the <c>InnerException</c> property.
		/// </exception>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public void Save(Entity pEntity)
		{
			try
			{
				if (pEntity.Id <= 0)
					DSAS.Session.SaveOrUpdate(pEntity);
			}
			catch (HibernateException vHx)
			{
				var vDx = new DataException(DataErrorCode.SAVE_ENTITY_FAILED, vHx);
				throw vDx;
			}
		}
	}

	/// <summary>
	/// EntityRepository(TEntity) brings strong typing to <c>EntityRepository</c>s.
	/// It encapsulates all strongly typed but common operations to <c>EntityRepository</c>s.
	/// </summary>
	/// <typeparam name="TEntity">The type of the <c>Entity</c> reposited.</typeparam>
	/// <version>1.0</version>
	/// <since>1.0</since>
	public class EntityRepository<TEntity> : EntityRepository where TEntity : Entity
    {
		/// <summary>
		/// Finds this <c>EntityRepository</c>'s <c>Entity</c> matching the given identifier
		/// from the persistence layer.
		/// </summary>
		/// <param name="pId">The <c>Entity</c> identifier.</param>
		/// <returns>The <c>Entity</c> having the given identifier or <c>null</c>.</returns>
		/// <exception cref="T:DataException">
		/// Thrown with an error code of <c>DataErrorCode.GET_ENTITY_FAILED</c>
		/// when an error is reported by NHibernate. The <c>HibernateException</c> is
		/// accessible via the <c>InnerException</c> property.
		/// </exception>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public TEntity FindById(int pId)
		{
			TEntity rEnt = null;
			
			try
			{
				rEnt = (TEntity)DSAS.Session.Get(typeof(TEntity), pId);
			}
			catch (HibernateException vHx)
			{
				var vDx = new DataException(DataErrorCode.GET_ENTITY_FAILED, vHx);
				throw vDx;
			}
			
			return rEnt;
		}
    }
}
